"""Allows easy run-time tracing of references to objects. Each 'sink' deploys
an 'xref' dictionary attribute which is updated whenever CrossReferences to
the object are created and destroyed. By reading the current value of 'xref',
one can tell which objects are referring the given object via the cross-
reference mechanism."""

import weakref

class CrossReference(object):
    
    def __init__(self, source, referencee):
        self._set(source, referencee)
        self._assemble()

    def __getstate__(self):
        return self.source(), self.referencee

    def __setstate__(self, state):
        source, referencee = state
        self._set(source, referencee)

    def _set(self, source, referencee):
        self.source = XRef(source)
        self.referencee = referencee

    def _assemble(self):
        if hasattr(self.referencee, "xref"):
            self.referencee.xref[self.source] = 1

    def __call__(self):
        return self.referencee

    def __del__(self):
        if hasattr(self.referencee, "xref"):
            try:
                del self.referencee.xref[self.source]
            except KeyError:
                print "CrossReference.__del__ failed for %r -> %r %r" % (self.source(), self.referencee, self.referencee.__dict__)


class XRef(object):
    """This internal type is used instead of weakref to monitor references
    without creating loops, also allowing cross-referenced objects to be saved
    with 'pickle'."""
    
    def __init__(self, referencee): self._set(referencee)

    def _set(self, referencee):
        if referencee is None: print "WARN > None referencee"
        self.ref = weakref.ref(referencee)
        self.id = id(referencee)

    def __getstate__(self): return (self.ref())
    def __setstate__(self, state): self._set(state)
    def __hash__(self): return self.id
    def __cmp__(self, other): return cmp(self.id, other.id)
    def __call__(self): return self.ref()

